﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BaseMethods;
using Pattern.Common;
using Pattern.Data;
using Pattern.Model;

namespace Pattern.Logic
{
    public static class RawData
    {
        public static void ReadAndSerialize()
        {
            ReadAndSerializeCardioData();
            ReadAndSerializeTaeData();
            ReadAndSerializeWineData();
        }
        private static void ReadAndSerializeCardioData()
        {
            const string cardioRawPath = @"D:\Dropbox\MAG\Dane\Cardio\CTG.csv";
            const string cardioXMLPath = @"D:\Dropbox\MAG\Dane\XML\Cardio.xml";

            var cardioData = GetSampleList(RawDataReader.ReadCardio, cardioRawPath,3);
            cardioData.Normalize();
            Serialization.Serialize(cardioXMLPath, cardioData);
        }

        private static void ReadAndSerializeTaeData()
        {
            const string taeRawPath = @"D:\Dropbox\MAG\Dane\TAE\tae.data";
            const string taeXMLPath = @"D:\Dropbox\MAG\Dane\XML\Tae.xml";

            var taeData = GetSampleList(RawDataReader.ReadTae, taeRawPath,3);
            taeData.Normalize();
            Serialization.Serialize(taeXMLPath, taeData);
        }

        private static void ReadAndSerializeWineData()
        {
            const string rWineRawPath = @"D:\Dropbox\MAG\Dane\WINE\winequality-red.csv";
            const string rWineXMLPath = @"D:\Dropbox\MAG\Dane\XML\wineR.xml";
            const string wWineRawPath = @"D:\Dropbox\MAG\Dane\WINE\winequality-white.csv";
            const string wWineXMLPath = @"D:\Dropbox\MAG\Dane\XML\wineW.xml";

            var data = GetSampleList(RawDataReader.ReadWine, rWineRawPath,10);
            data.Normalize();
            Serialization.Serialize(rWineXMLPath, data);
            data = GetSampleList(RawDataReader.ReadWine, wWineRawPath,10);
            data.Normalize();
            Serialization.Serialize(wWineXMLPath, data);
        }

        private static SampleList GetSampleList(Func<string, List<Sample>> dataFunc, string path,int numberofClasses)
        {
            var data = dataFunc(path);
            var sampleList = new SampleList { Samples = data,NumberOfClasses = numberofClasses};
            return sampleList;
        }

        private static void Normalize(this SampleList list)
        {
            var max = new double[list.Samples[0].Features.Length];
            var min = new double[max.Length];
            for (int i = 0; i < max.Length; i++)
            {
                max[i] = double.MinValue;
                min[i] = double.MaxValue;
            }

            foreach (var sample in list.Samples)
            {
                for (int i = 0; i < sample.Features.Length; i++)
                {
                    var feature = sample.Features[i];
                    max[i] = Math.Max(max[i], feature);
                    min[i] = Math.Min(min[i], feature);
                }
            }
            var dif = new double[max.Length];
            for (int i = 0; i < dif.Length; i++)
            {
                dif[i] = max[i] - min[i];
            }

            foreach (var sample in list.Samples)
            {
                for (int i = 0; i < sample.Features.Length; i++)
                {
                    sample.Features[i] = (sample.Features[i] - min[i]) / dif[i];
                    if (double.IsNaN(sample.Features[i]))
                    {
                        sample.Features[i] = 0;
                    }
                }
            }
        }

        public static void GenerateAndSerialize()
        {
            const int N = 1000;
            const int numberOfFeatures = 2;
            const int numberOfClasses = 3;
            var rnd = new Random();

            // Rozłączne
            //1
            var c1Devf1 = 1.0;
            var c1Meanf1 = 0.0;

            var c2Devf1 = 1.0;
            var c2Meanf1 = 5.0;

            var c3Devf1 = 1.0;
            var c3Meanf1 = 10.0;

            var c1Devf2 = 1.0;
            var c1Meanf2 = .0;

            var c2Devf2 = 1.0;
            var c2Meanf2 = 5.0;

            var c3Devf2 = 1.0;
            var c3Meanf2 = 10.0;
            var @params = new[] { new[] { c1Devf1, c1Meanf1, c1Devf2, c1Meanf2 }, new[] { c2Devf1, c2Meanf1, c2Devf2, c2Meanf2 }, new[] { c3Devf1, c3Meanf1, c3Devf2, c3Meanf2 } };
            var prob = new[] { 1.0 / 3, 1.0 / 3 };
            var sampleList = GetArtificialSamples(rnd, numberOfClasses, numberOfFeatures, N, prob, @params);
            const string a1 = @"D:\Dropbox\MAG\Dane\XML\A1.xml";
            Serialization.Serialize(a1, sampleList);

            //2
            c1Devf1 = 1.0;
            c1Meanf1 = 0.0;

            c2Devf1 = 1.0;
            c2Meanf1 = 5.0;

            c3Devf1 = 1.0;
            c3Meanf1 = 10.0;

            c1Devf2 = 1.0;
            c1Meanf2 = 0;
            c2Devf2 = 1.0;
            c2Meanf2 = 2;
            c3Devf2 = 1.0;
            c3Meanf2 = 4;
            @params = new[] { new[] { c1Devf1, c1Meanf1, c1Devf2, c1Meanf2 }, new[] { c2Devf1, c2Meanf1, c2Devf2, c2Meanf2 }, new[] { c3Devf1, c3Meanf1, c3Devf2, c3Meanf2 } };
            prob = new[] { 1.0 / 3, 1.0 / 3 };
            sampleList = GetArtificialSamples(rnd, numberOfClasses, numberOfFeatures, N, prob, @params);
            const string a2 = @"D:\Dropbox\MAG\Dane\XML\A2.xml";
            Serialization.Serialize(a2, sampleList);

            // Zachodzące
            c1Devf1 = 1.0;
            c1Meanf1 = 0.0;

            c2Devf1 = 1.0;
            c2Meanf1 = 5.0;

            c3Devf1 = 1.0;
            c3Meanf1 = 10.0;


            c1Devf2 = 1.0;
            c1Meanf2 = 0;
            c2Devf2 = 1.0;
            c2Meanf2 = 0;
            c3Devf2 = 1.0;
            c3Meanf2 = 0;
            @params = new[] { new[] { c1Devf1, c1Meanf1, c1Devf2, c1Meanf2 }, new[] { c2Devf1, c2Meanf1, c2Devf2, c2Meanf2 }, new[] { c3Devf1, c3Meanf1, c3Devf2, c3Meanf2 } };
            prob = new[] { 1.0 / 3, 1.0 / 3 };
            sampleList = GetArtificialSamples(rnd, numberOfClasses, numberOfFeatures, N, prob, @params);
            const string b1 = @"D:\Dropbox\MAG\Dane\XML\B1.xml";
            Serialization.Serialize(b1, sampleList);

            c1Devf1 = 1.0;
            c1Meanf1 = 0;
            c2Devf1 = 1.0;
            c2Meanf1 = 0;
            c3Devf1 = 1.0;
            c3Meanf1 =1.5;


            c1Devf2 = 1.0;
            c1Meanf2 = 0;
            c2Devf2 = 1.0;
            c2Meanf2 = 3;
            c3Devf2 = 1.0;
            c3Meanf2 = 1.5;
            @params = new[] { new[] { c1Devf1, c1Meanf1, c1Devf2, c1Meanf2 }, new[] { c2Devf1, c2Meanf1, c2Devf2, c2Meanf2 }, new[] { c3Devf1, c3Meanf1, c3Devf2, c3Meanf2 } };
            prob = new[] { 1.0 / 3, 1.0 / 3 };
            sampleList = GetArtificialSamples(rnd, numberOfClasses, numberOfFeatures, N, prob, @params);
            const string b2 = @"D:\Dropbox\MAG\Dane\XML\B2.xml";
            Serialization.Serialize(b2, sampleList);


            c1Devf1 = 1.0;
            c1Meanf1 = 0;
            c2Devf1 = 1.0;
            c2Meanf1 = 1;
            c3Devf1 = 1.0;
            c3Meanf1 = 2;


            c1Devf2 = 1.0;
            c1Meanf2 = 0;
            c2Devf2 = 1.0;
            c2Meanf2 = 0;
            c3Devf2 = 1.0;
            c3Meanf2 = 0;
            @params = new[] { new[] { c1Devf1, c1Meanf1, c1Devf2, c1Meanf2 }, new[] { c2Devf1, c2Meanf1, c2Devf2, c2Meanf2 }, new[] { c3Devf1, c3Meanf1, c3Devf2, c3Meanf2 } };
            prob = new[] { 1.0 / 3, 1.0 / 3 };
            sampleList = GetArtificialSamples(rnd, numberOfClasses, numberOfFeatures, N, prob, @params);
            const string c1 = @"D:\Dropbox\MAG\Dane\XML\C1.xml";
            Serialization.Serialize(c1, sampleList);
            // Okręgi
            // ToDo: Never :)

        }

        private static SampleList GetArtificialSamples(Random rnd, int numberOfClasses, int numberOfFeatures, int N, double[] prob, double[][] @params)
        {
            var randoms = rnd.NormalArray(numberOfFeatures * N);
            var sampleList = new List<Sample>();
            for (int n = 0; n < N; n++)
            {
                var p = rnd.NextDouble();
                var c = numberOfClasses - 1;
                for (int pr = 0; pr < prob.Length; pr++)
                {
                    if (p < prob.Take(pr + 1).Sum())
                    {
                        c = pr;
                        break;
                    }
                }
                var sample = new Sample { Class = c + 1, Features = new double[numberOfFeatures] };
                for (int f = 0; f < numberOfFeatures; f++)
                {
                    var i = f * n + f;
                    var j = f == 0 ? 0 : 2;
                    sample.Features[f] = randoms[i] * @params[c][0 + j] + @params[c][1 + j]; //x*dev+mean
                }
                sampleList.Add(sample);
            }
            return new SampleList { Samples = sampleList,NumberOfClasses = numberOfClasses};
        }
    }
}
